package com.steptime.jdk8;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.RandomUtil;
import org.junit.Test;

import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 描述：Stream使用方法
 */
public class StreamDemo {
    /**
     * 创建Steam对象的四种方法
     */
    @Test
    public void generate() {
        //第一种 通过Stream接口的of静态方法创建一个流
        Stream<String> stream = Stream.of("taobao");
        System.out.println(stream.findFirst());
        //第二种 通过Arrays类的stream方法，实际上第一种of方法底层也是调用的Arrays.stream(values);
        String[] array = new String[]{"hello", "world", "helloworld"};
        Stream<String> stream3 = Arrays.stream(array);
        //第三种 通过集合的stream方法，该方法是Collection接口的默认方法，所有集合都继承了该方法
        Stream<String> stream2 = ListUtil.list(false, "hello", "world", "helloworld").stream();
        //第四种 通过Stream.generate 静态方法创建
        Stream<Integer> generateStream = Stream.generate(() -> RandomUtil.randomInt(10));
        System.out.println(generateStream.findFirst());


    }

    /**
     * filter方法
     * 示例：去掉集合中值为null的元素
     */
    @Test
    public void filterDemo() {
        List<Integer> intList = ListUtil.list(false, 1, null, 3, null, 5);
        System.out.println(intList.stream().filter(num -> num != null));


    }

    /**
     * sort方法
     * 示例：对集合中的元素进行排序
     */
    @Test
    public void sortDemo() {
        List<Integer> intList = ListUtil.list(false, 1, 5, 3);
        System.out.println(intList.stream().sorted().collect(Collectors.toList()));


    }

    /**
     * map方法
     * 描述：map方法的使用，这里我们使用了Stream的map方法，
     * map方法接收一个Function函数式接口实例，这里的map和Hadoop中的map概念完全一致，
     * 对每个元素进行映射处理。
     * 示例：将流中字符全部转成大写返回一个新的集合
     */
    @Test
    public void mapDemo() {
        List<String> list = Arrays.asList("hello", "world", "helloworld");
        List<String> collect = list.stream().map(s -> s.toUpperCase())
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    /**
     * flatMap方法
     * 描述：flatMap方法，可以将多个容器的元素全部映射到一个容器中，即为扁平的map。
     *
     */
    @Test
    public void flatMapDemo() {
        Stream<List<Integer>> listStream =
                Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
        List<Integer> collect1 = listStream.flatMap(theList -> theList.stream()).
                map(integer -> integer * integer).
                collect(Collectors.toList());
        System.out.println(collect1);
    }

    /**
     * 统计信息的方法
     * 描述：将list1中的数据取出大于2的，
     * 每个数进行平方计算，skip(2)忽略前两个，limit(2)再取出前两个，
     * summaryStatistics对取出的这两个数计算统计数据。
     * mapToInt接收一个ToIntFunction类型，也就是接收一个参数返回值是int类型。
     */
    @Test
    public void summaryDemo() {
        //同时获取最大 最小 平均值等信息
        List<Integer> list1 = Arrays.asList(1, 3, 5, 7, 9, 11);
        IntSummaryStatistics statistics = list1.stream().
                filter(integer -> integer > 2).
                mapToInt(i -> i * 2).skip(2).limit(2).
                summaryStatistics();
        System.out.println(statistics.getMax());//18
        System.out.println(statistics.getMin());//14
        System.out.println(statistics.getAverage());//16
    }

    /**
     * iterate方法
     * 描述：iterate方法有两个参数，第一个是seed也可以称作种子，第二个是一个UnaryOperator，
     * UnaryOperator实际上是Function的一个子接口，和Funciton区别就是参数和返回类型都是同一种类型
     */
    @Test
    public void iterateDemo() {
        Stream.iterate(1,item -> item + 2).limit(10).forEach(System.out::println);
    }

    /**
     * Stream分组与分区详解
     * 描述：在实际开发中，对于将一个集合的内容进行分组或分区这种需求也非常常见，
     * 所以我们继续学习下Collectors类中的groupingBy和partitioningBy方法。
     * 示例：
     * groupby:假如现在有一个实体Student，我们现在按Student的score进行分组，
     * 如果使用sql来表示就是select * from student group by score; 再看下使用Stream的方式
     * partitioningBy：针对上面的Student，我们现在再加一个需求，分别统计一下及格和不及格的学生（分数是否>=60）
     * 这时候符合Stream分区的概念了，Stream分区会将集合中的元素按条件分成两部分结果，
     * key是Boolean类型，value是结果集，满足条件的key是true，我们看下示例。
     */
    @Test
    public void groupDemo() {
        /**
         * groupingBy接收一个Function类型的变量classifier，classifier被称作分类器，
         * 收集器会按着classifier作为key对集合元素进行分组，然后返回Collector收集器对象
         */
        List<Student> students = ListUtil.of(
                new Student("tom",80),
                new Student("jim",90),
                new Student("lilei",90),
                new Student("hanmei",96));

        Map<Integer, List<Student>> collMap = students.stream()
                .collect(Collectors.groupingBy(Student::getScore));
        System.out.println(collMap);
        /**
         * 那如果分组后，想求出每组元素的数量，就需要借助groupingBy另一个重载的方法
         * 第二个参数downstream还是一个收集器Collector对象，
         * 也就是说我们可以先将classifier作为key进行分组，然后将分组后的结果交给downstream收集器再进行处理
         */
        Map<Integer, Long> collect1 = students.stream()
                .collect(Collectors.groupingBy(Student::getScore, Collectors.counting()));
        System.out.println(collect1);

        //分区
        Map<Boolean, List<Student>> collect3 = students.stream().
                collect(Collectors.partitioningBy(student -> student.getScore() >= 90));
        System.out.println(collect3.get(true));//输出及格的Student
        System.out.println(collect3.get(false));//输出不及格的Student

    }
}
